// -*- swift -*-
// RUN: rm -rf %t ; mkdir -p %t
// RUN: %S/../../utils/gyb %s -o %t/RangeReplaceable.swift
// RUN: %S/../../utils/line-directive %t/RangeReplaceable.swift -- %target-build-swift %t/RangeReplaceable.swift -o %t/a.out
// RUN: %S/../../utils/line-directive %t/RangeReplaceable.swift -- %target-run %t/a.out
// REQUIRES: executable_test

// This is a workaround to avoid the compiler generating too large code: rdar://problem/24330686
// FIXME: remove the following line when the compiler problem is fixed.
// REQUIRES: swift_test_mode_optimize_none

import StdlibUnittest
import StdlibCollectionUnittest


var RangeReplaceableTestSuite = TestSuite("RangeReplaceable")

RangeReplaceableTestSuite.test("append/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.append(OpaqueValue(2))
  expectCustomizable(tester, tester.log.append)
}

RangeReplaceableTestSuite.test("appendContentsOf/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.append(contentsOf: [ 2, 3 ].map(OpaqueValue.init))
  expectCustomizable(tester, tester.log.appendContentsOf)
}

RangeReplaceableTestSuite.test("insert/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.insert(OpaqueValue(2), at: tester.base.startIndex)
  expectCustomizable(tester, tester.log.insert)
}

RangeReplaceableTestSuite.test("insert(contentsOf:at:)/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.insert(
    contentsOf: [ 2, 3 ].map(OpaqueValue.init),
    at: tester.base.endIndex)
  expectCustomizable(tester, tester.log.insertContentsOf)
}

RangeReplaceableTestSuite.test("remove(at:)/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.remove(at: tester.base.startIndex)
  expectCustomizable(tester, tester.log.removeAt)
}

RangeReplaceableTestSuite.test("removeLast/whereIndexIsBidirectional/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  _ = tester.removeLast()
  expectCustomizable(tester, tester.log._customRemoveLast)
}

RangeReplaceableTestSuite.test("removeLast(n: Int)/whereIndexIsBidirectional/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  _ = tester.removeLast(1)
  expectCustomizable(tester, tester.log._customRemoveLastN)
}

RangeReplaceableTestSuite.test("removeSubrange/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester(
    [ 1, 2, 3 ].map(OpaqueValue.init))
  tester.removeSubrange(tester.base.startIndex..<tester.base.endIndex)
  expectCustomizable(tester, tester.log.removeSubrange)
}

RangeReplaceableTestSuite.test("removeFirst/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.removeFirst()
  expectCustomizable(tester, tester.log.removeFirst)
}

RangeReplaceableTestSuite.test("removeFirst(n)/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.removeFirst(1)
  expectCustomizable(tester, tester.log.removeFirstN)
}

RangeReplaceableTestSuite.test("removeAll/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester([OpaqueValue(1)])
  tester.removeAll(keepingCapacity: false)
  expectCustomizable(tester, tester.log.removeAll)
}

RangeReplaceableTestSuite.test("reserveCapacity/dispatch") {
  var tester = RangeReplaceableCollectionLog.dispatchTester(Array<Int>())
  tester.reserveCapacity(10)
  expectCustomizable(tester, tester.log.reserveCapacity)
}

func identity(_ element: OpaqueValue<Int>) -> OpaqueValue<Int> {
  return element
}
func identityEq(_ element: MinimalEquatableValue) -> MinimalEquatableValue {
  return element
}

% from itertools import product
% traversals = ['Forward', 'Bidirectional', 'RandomAccess']
% base_kinds = ['Defaulted', 'Minimal']
% collections = [
%   ('{}{}RangeReplaceableCollection'.format(base_kind, traversal), traversal)
%     for base_kind, traversal in product(base_kinds, traversals)
% ] + [
%   ('Defaulted{}RangeReplaceableSlice'.format(traversal), traversal)
%     for traversal in traversals
% ]

% for Base, traversal in collections:
%     collection_or_slice = 'Slice' if 'Slice' in Base else 'Collection'
do {
  var resiliencyChecks = CollectionMisuseResiliencyChecks.all
  resiliencyChecks.creatingOutOfBoundsIndicesBehavior = .expectationFailure

  RangeReplaceableTestSuite.add${traversal}RangeReplaceable${collection_or_slice}Tests(
    makeCollection: { (elements: [OpaqueValue<Int>]) in
      return ${Base}(elements: elements)
    },
    wrapValue: identity,
    extractValue: identity,
    makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
      return ${Base}(elements: elements)
    },
    wrapValueIntoEquatable: identityEq,
    extractValueFromEquatable: identityEq,
    resiliencyChecks: resiliencyChecks)

  RangeReplaceableTestSuite.add${traversal}RangeReplaceable${collection_or_slice}Tests(
    makeCollection: { (elements: [LifetimeTracked]) in
      return ${Base}(elements: elements)
    },
    wrapValue: { (element: OpaqueValue<Int>) in LifetimeTracked(element.value) },
    extractValue: { (element: LifetimeTracked) in OpaqueValue(element.value) },
    makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in
      // FIXME: use LifetimeTracked.
      return ${Base}(elements: elements)
    },
    wrapValueIntoEquatable: identityEq,
    extractValueFromEquatable: identityEq,
    resiliencyChecks: resiliencyChecks)
}

% end

runAllTests()

